package com.mazaiting.classfile.constant;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import com.mazaiting.classfile.ClassReader;
import com.mazaiting.classfile.constant.entity.NameAndType;
import com.mazaiting.classfile.constant.info.*;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.Objects;

/**
 * 常量池
 * 第一， 表头给出的常量池大小比实际大1。假设表头给出的值是n，那么常量池的实际大小是n–1。
 * 第二，有效的常量池索引是1~n–1。0是无效索引，表示不指向任何常量。
 * 第三，CONSTANT_Long_info 和 CONSTANT_Double_info各占两个位置
 *
 * @author mazaiting
 * @since 2021/8/11
 */
@Getter
@Slf4j
public class ConstantPool {
    /**
     * 常量池数量
     */
    private final int count;

    /**
     * 常量池信息
     */
    private final ConstantInfo[] constantInfos;

    public ConstantPool(int count) {
        this.count = count;
        constantInfos = new ConstantInfo[this.count];
    }

    /**
     * 解析常量池
     *
     * @param reader 类读取工具
     */
    public void read(ClassReader reader) {
        // 遍历
        for (int i = 1; i < count; i++) {
            short tag = reader.readU1ToUnsignedByte();
            ConstantInfo constantInfo = ConstantFactory.newConstantInfo(this, tag);
            Assert.notNull(constantInfo, () -> new RuntimeException("未知 tag: " + tag));
            Objects.requireNonNull(constantInfo).readInfo(reader);
            constantInfos[i] = constantInfo;
            //log.debug(i + " : " + constantInfo);
            // 判断是否为占用 8 字节
            if (ConstantFactory.checkLongOrDouble(tag)) {
                i++;
            }
        }
    }

    /**
     * 根据索引获取常量信息
     *
     * @param index 索引
     * @return 常量信息
     */
    public ConstantInfo getConstantInfo(int index) {
        ConstantInfo constantInfo = constantInfos[index];
        if (ObjectUtil.isNotEmpty(constantInfo)) {
            return constantInfo;
        }
        throw new RuntimeException("Invalid constant pool index!");
    }

    /**
     * 获取类名
     *
     * @param index 类索引
     * @return 类名
     */
    public String getClassName(int index) {
        ConstantClassInfo classInfo = (ConstantClassInfo) this.getConstantInfo(index);
        return classInfo.name();
    }

    /**
     * 获取名称和类型
     *
     * @param index 索引
     * @return 名称和类型
     */
    public NameAndType getNameAndType(int index) {
        ConstantNameAndTypeInfo nameAndTypeInfo = (ConstantNameAndTypeInfo) this.getConstantInfo(index);
        NameAndType nameAndType = new NameAndType();
        nameAndType.setName(this.getUtf8(nameAndTypeInfo.getNameIndex()));
        nameAndType.setType(this.getUtf8(nameAndTypeInfo.getDescriptorIndex()));
        return nameAndType;
    }

    /**
     * 获取字符串
     *
     * @param index 索引
     * @return 字符串
     */
    public String getUtf8(int index) {
        ConstantUtf8Info utf8Info = (ConstantUtf8Info) this.getConstantInfo(index);
        return utf8Info.string();
    }
}


















